#!/usr/bin/env perl
#===============================================================================
#
#  FILE:  Docker.pl
#
#  DESCRIPTION:  利用Docker remote API对docker进行基本的管理
#
#        FILES:  ---
#         BUGS:  ---
#        NOTES:  ---
#       AUTHOR:  zoufuxing@line0.com
#      COMPANY:  南京零号线电子商务有限公司
#      VERSION:  1.0
#      CREATED:  2016/06/14 10时52分09秒
#     REVISION:  ---
#===============================================================================

use warnings;
use strict;
use Data::Dumper;
use Getopt::Std;
use Getopt::Long;
use POSIX qw(strftime);
#use Pod::Usage;
#use JSON::RPC::Client;


use constant { 
    #real true false, compatible with encode_json decode_json for later (we don't want field:false... will be field:0...)
    true =>  bless( do{\(my $o = 1)}, 'JSON::PP::Boolean' ),
    false =>  bless( do{\(my $o = 0)}, 'JSON::PP::Boolean' )
    };


BEGIN {push @INC,"./lib"};
use DockerOperation;
use MySQLOP;

#my $man = 0;
#my $help = 0;
#GetOptions('help|?' => \$help, man => \$man) or pod2usage(2);
#pod2usage(1) if $help;
#pod2usage(-exitval => 0, -verbose => 2) if $man;


#初始化实例
my $dop = DockerOperation->new({
    
    'base_url' => 'http://192.168.1.186:2375',
    #'base_url' => 'http://172.10.10.45:4242',
				    }
    );


#----创建数据库连接------
my $mop = MySQLOP->new({
    'username' => 'dop',
    'passwd' => 'Urfresh_2016',
    'dbname' => 'docker',
    #'host' => '191.168.11.29'
    'host' => '192.168.1.74',
    'port' => '4306'
		       }    
    );

#---------用于打印容器列表---------------
#my ($name,$cid,$image_id,$status,$expose_port,$create_time);
my ($name,$cid,$image_id,$ip,$expose_port,$create_time);


#----------全局获取数据库连接句柄-------
my $dbh = $mop->_DB_Connect;

#----------命令行参数----------
my $create_para_ref = undef;
my $list_para_ref = undef;
my $group_list_ref = undef;
my $del_para_ref = undef;
my $exec_para_ref = undef;
my $op_start = undef;
my $op_stop = undef;
my $op_restart = undef;

GetOptions('help' => \&help,
           'create=s%{1,5}' => \$create_para_ref,
	   'list=s%{0,1}' => \$list_para_ref,
	   'grouplist=s%{0,1}' => \$group_list_ref,
	   'delete=s%{1,}' => \$del_para_ref,
	   'exec=s%{2,}' => \$exec_para_ref,
	   'start=s' => \$op_start,
	   'stop=s' => \$op_stop,
	   'restart=s' => \$op_restart,
);


sub help
{
    #print "This is help function!\n";
    print " list status=<0|1|2> group=<group name> \n";
    print "     default 0,show all active container;1: show all;2:show unactive container.\n";
    print " create name=name image=<image ID or image name> group=<group name>\n";
    print " start|stop|restart  <container id or name>\n";
    print " del name=<container id or name>\n";
    exit;
}


#-------------如果没有任何的指定，则输出help---------
#&help if(!$create_para_ref || !$list_para_ref);


#if($exec_para_ref){
#    print "Execute cmd: $exec_para_ref->{'cmd'}\n";
#    print Dumper($exec_para_ref);
#    &Exec_Cmd($exec_para_ref->{'name'},$exec_para_ref->{'cmd'});
#    exit;
#}


#---------------打印分组列表----------------------
if($group_list_ref){
    my $group_list_ref = $mop->_Group_query($dbh);
    print "  Group Name  \n";
    print "--------------\n";
    #print Dumper(@$group_list_ref);
    foreach(@$group_list_ref){
	print "  $_   \n";
    }

    exit;
}




#----------------打印容器列表----------------------
if($list_para_ref){

    #----按照分组显示容器----
    if($list_para_ref->{'group'}){
	my $list_group_ref = $mop->_List_By_Group($dbh,$list_para_ref->{'group'});
	#print Dumper($list_group_ref);
        #exit;

	
	foreach my $hash_ref (@{$list_group_ref}){
	    #print Dumper($hash_ref);
            #exit;
	    
	    #将unixtime转换
	    my $create_time = strftime "%Y-%m-%d %H:%M:%S",localtime($hash_ref->{'create_time'});
	  
            $hash_ref->{'export_application_port'} = 'NULL' if(!$hash_ref->{'export_application_port'});
	    my $export_port = "22"."->".$hash_ref->{'export_ssh_port'}." "."8080"."->".$hash_ref->{'export_application_port'};
	    &Format_Output($hash_ref->{'name'},$hash_ref->{'cid'},$hash_ref->{'image_name'},$hash_ref->{'ip_address'},$create_time,$export_port);
	}
	
	exit;
    }
    
    #用于控制未激活容器的输出列表
    my $exit_create_show = 0;
    my $array_ref;

    #cstatus用于标识容器的状态默认值是0，只列出激活状态的容器，1为列出所有，2为只列出关闭的容器
    my $cstatus = 0;
    $cstatus = $list_para_ref->{'status'} if($list_para_ref->{'status'});
    
    
    if($cstatus == 2){
	$exit_create_show = 1;
	$cstatus = 1;
    }
    #----$status的值为0 1 2-------
    # 0: 状态为up的容器列表,默认值
    # 1: 所有的容器列表，包括Exit、Created状态的
    # 2: 所有未激活的容器,包括Exit、Created状态的
    
    my $clist_ref = &Container_List($cstatus);
    
    #将端口的映射关系整合成一个字符串
    my $expose_port_format;
    #print "\$cstatus =  $cstatus\n";


    foreach (@$clist_ref){
	$array_ref = $_;
	my @status_value;

        #print Dumper($array_ref);
        #print $array_ref->{'NetworkSettings'}->{'Networks'}->{'bridge'}->{'IPAddress'}."\n";

	#print Dumper($array_ref);
	#----取出name的第一个字符'/'
	my @name = split('/',$array_ref->{'Names'}[0]);
		
	#-------当容器为非激活状态，Ports的匿名数组为空-------------
	my $ports_arr = @{$array_ref->{'Ports'}};
	
        $create_time = strftime "%Y-%m-%d %H:%M:%S",localtime($array_ref->{'Created'});
	
	#print Dumper($array_ref->{'Ports'});
	
	if($exit_create_show == 1){
	    #@status_value = ($name[1],substr($array_ref->{'Id'},0,12),substr($array_ref->{'Image'},0,12),$array_ref->{'Status'},$create_time);
	    @status_value = ($name[1],substr($array_ref->{'Id'},0,12),substr($array_ref->{'Image'},0,12),$array_ref->{'NetworkSettings'}->{'Networks'}->{'bridge'}->{'IPAddress'},$create_time);

	    if($array_ref->{'Status'} =~ /(Exited|Created)/){
		#print substr($array_ref->{'Id'},0,12)."\n";
		&Format_Output(@status_value);
	    }
	}else{
	    #print substr($array_ref->{'Id'},0,12)."\n";
	    @status_value = ($name[1],substr($array_ref->{'Id'},0,12),substr($array_ref->{'Image'},0,12),$array_ref->{'NetworkSettings'}->{'Networks'}->{'bridge'}->{'IPAddress'},$create_time);

	    #-------$array_ref->{'Ports'}是一个匿名数组,$ports_arr是其元素的个数，当个数为0时，说明该容器是未激活状态-----
	    if($ports_arr != 0){
		foreach (@{$array_ref->{'Ports'}}){
		    $expose_port_format =  $_->{'PrivatePort'}."->"."$_->{'PublicPort'} ".$expose_port_format;
		}
	    }
	   #print $expose_port_format."\n";
	    #exit;
	    push(@status_value,$expose_port_format);
	    &Format_Output(@status_value);
	}
    }

}


#-----------------创建新的容器----------------------
if($create_para_ref){
    
    #必须指定容器名
    die "The container name must be not NULL!\n"  if(!$create_para_ref->{'name'});

    #必须指定容器所属组
    die "The container group must be not NULL!\n" if(!$create_para_ref->{'group'});

    #如果批量创建容器
    my $count = $create_para_ref->{'count'};
    #如果不指定数量，默认为1
    $count = 1 if(!$create_para_ref->{'count'});

    my $name = $create_para_ref->{'name'};
    #批量开启容器
    for(my $i = 1;$i <= $count;$i++){
	#---
	if($count >= 2){
	     $create_para_ref->{'name'} = $name.$i;
	     $create_para_ref->{'hostname'} = $name.$i;
        }
	#-------通过获取容器的底层信息来判断name是否存在-------
	my $check_name = $dop->_Container_info_by_id_name_ref($create_para_ref->{'name'});

	#如果已经存在了相同的容器名，则跳过
	if($check_name->{'Id'}){
	    print "The Container $create_para_ref->{'name'} exits,use another name!\n";
	    next;
	}
	
	&Create_Container($create_para_ref);
    }
    exit;
}


#--------------容器启动/关闭/重启-----------------
if($op_start || $op_stop || $op_restart) {
    my $operation = undef;
    my $cid = undef;

    if($op_start){
	$operation = 'start';
	$cid = $op_start;
    }

    if($op_stop){
	$operation = 'stop';
	$cid = $op_stop;
    }


    if($op_restart){
	$operation = 'restart';
	$cid = $op_restart;
    }

    print $operation."\n";
    print $cid."\n";
    &Start_Stop_Restart_Container($cid,$operation);
    
    exit;
}

#---------------删除指定容器----------------------
if($del_para_ref){
    die "You must specific container ID or name!\n" if(!$del_para_ref->{'name'});

    print "Do you sure delete container:  $del_para_ref->{'name'} <y/n>";
    my $a = <STDIN>;
    #print $a;
    chomp($a);


    #将name转换成ID
    my $container_array_ref = $dop->_Container_List;
    my $info_ref = undef;
    
    foreach my $array_ref (@$container_array_ref){
	
	#当条件成立后，立刻退出循环
	if( ($array_ref->{'Names'}[0] =~ $del_para_ref->{'name'}) ){
	    #$info_ref = $array_ref;
	    $del_para_ref->{'name'} = substr($array_ref->{'Id'},0,12);
	    last;
	}
    }

    #print $del_para_ref->{'name'}."\n";
    
    &Remove_Container($del_para_ref->{'name'}) if($a eq 'y')||($a eq 'Y');
    exit;
}



#----------------将容器信息写入数据库------------------------
sub Write_Container_Info
{
    my ($cid,$business_group) = @_;
    
    $business_group = 'test' if(!$business_group);
    my $container_inspect_info = $dop->_Inspect_Container($cid);
    
    my $container_info = $dop->_Container_info_by_id_name_ref($cid);

    $mop->_New_Insert($dbh,$container_inspect_info,$container_info,$business_group);
}


#&Write_Container_Info;

#my $rekk = $mop->_Export_Port($dbh);
#print Dumper($rekk);
#exit;

#--------------格式化输出---------------------------------
sub Format_Output
{
    my ($name,$cid,$image_id,$ip_address,$create_time,$expose_port) = @_;
    
    $^ = 'STDOUT_TOP';
    $~ = 'STDOUT';
    $= = 39;
    write;

    format STDOUT_TOP =
@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
"Page $%"
                           Docker Container List
  Name                 ID             Image              IP                  Create              Expose Port
---------------------------------------------------------------------------------------------------------------------------
.

format STDOUT =
@<<<<<<<<<<<<<<<<< @|||||||||||| @||||||||||||||||@|||||||||||||||||| @||||||||||||||||||||||| @>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
$name,  $cid,  $image_id,   $ip_address,   $create_time,    $expose_port
.

}


#---------------容器列表-----------------------------------
#  list_para_ref： 命令行参数多hash引用
sub Container_List
{
    my $status = shift;
    
    my $clist = $dop->_Container_List($status);
    return $clist;
}


#----------------生产容器-----------------------------------
# 需要指定的参数:
# business_group： 容器所属的业务组
# $tomcat_port,$ssh_port会到数据库里取
sub Create_Container
{
    #my ($hostname,$image,$name,$business_group) = @_;
    my $para_ref = shift;
    my $port_ref = $mop->_Export_Port($dbh);

    my $tomcat_port = $port_ref->{'tomcat_port'};
    my $ssh_port =  $port_ref->{'ssh_port'};
    my $dubbo_port = $port_ref->{'dubbo_port'};

    #print $para_ref->{'name'}."\n";
    #exit;

    my $hostname = $para_ref->{'hostname'};
    my $image = $para_ref->{'image'};
    my $name = $para_ref->{'name'};
    my $group = $para_ref->{'group'};
    
    #指定默认的镜像
    $image = 'common:v1' if(!$image);
    #$image = 'line0/jdk16' if(!$image);

    
    my $hash_ref = {
	Hostname => $hostname,
	#Cmd => ["/bin/bash"],
	Cmd => ["/usr/sbin/sshd","-D"],
	Image => $image,
	Names => $para_ref->{'name'},
	ExposedPorts => {
	    "8080/tcp" =>  {},
	    "22/tcp" => {},
	    #"3306/tcp" => {},
	    "20880/tcp" => {},
	    #"6379/tcp" => {},
	    #"6380/tcp" => {},
	    #"6381/tcp" => {},
	},

	AttachStdin => true,
	AttachStdout => true,
	AttachStderr => true,
	Tty => true,
	
	Dns => ["192.168.1.74"],
	Volumes => { "/opt/logs/$group/$name" => {},
		     "/etc/localtime" => {"ro"},
		     "/sys/fs/cgroup/" => {"ro"},
		     "/opt/share" => {"ro"}
	},
	HostConfig => { Binds => ["/opt/logs/$group/$name:/opt/software/tomcat/logs","/opt/share:/opt/share","/etc/localtime:/etc/localtime","/sys/fs/cgroup/:/sys/fs/cgroup/"],
		Memory => 8294967296,
		ExtraHosts => [],
		CpusetCpus => "0,1",
		CpuQuota => 50000,
		Privileged => true,
                StorageOpt => {"size" => "100G"},
		Ulimits => [{"Name" => "nofile","Soft" => 65536,"Hard" => 65536}],
		PortBindings => { 
		    "8080/tcp" =>  [{"HostIp" =>  "0.0.0.0", "HostPort" =>  $tomcat_port }], 
		    "22/tcp" =>  [{"HostIp" =>  "0.0.0.0", "HostPort" =>  $ssh_port }],
		    "20880/tcp" =>  [{"HostIp" =>  "0.0.0.0", "HostPort" =>  $dubbo_port }],
		    #"6379/tcp" =>  [{"HostIp" =>  "0.0.0.0", "HostPort" =>  "6379" }],
		    #"6380/tcp" =>  [{"HostIp" =>  "0.0.0.0", "HostPort" =>  "6380" }],
		    #"6381/tcp" =>  [{"HostIp" =>  "0.0.0.0", "HostPort" =>  "6381" }],
		},
},
    };

    #print Dumper($hash_ref);
    #开始创建容器
    my $ret = $dop->_Create_Container_Native($hash_ref);

    if(!$ret->{'is_success'}){
	#print Dumper($ret);
	print "Create Container fail!\n";
	exit;
    }

    #获取容器ID
    my $cid = $ret->{'content'}->{'Id'};

    #启动容器
    $ret = $dop->_Start_Stop_Kill_Restart_Contaner($cid,"start");
    if($ret->{'_rc'} != 204)
    {
	print "Start Container $cid Fail\n";
	print $ret->{'_content'}."\n";
	exit;
    }

    #重命名容器，默认的随机的名字，如果名字有重复，则随机
    $dop->_Rename_Container($cid,$para_ref->{'name'});

    #将容器信息写入数据库
    &Write_Container_Info($cid,$para_ref->{'group'});
}


#-------启动/关闭/重启---------------
sub Start_Stop_Restart_Container
{
    my ($cid,$operation) = @_;

    my $ret = $dop->_Start_Stop_Kill_Restart_Contaner($cid,$operation);

    #print Dumper($ret);
    my $ret_code = $ret->{_rc};

    #print $ret_code."\n";
    
    if($ret_code == 304){
    	print "Container $cid already $operation!\n";
    }elsif($ret_code == 404){
    	print "No such container!\n";
	exit;
    }elsif($ret_code == 500){
    	print "Server error!\n";
	exit;
    }else{
    	print "$operation container success!\n";
    }
    
}


#--------------删除容器--------------
sub Remove_Container
{
    my $cid = shift;

    #移除容器之前，先将其关闭,等待5s
    my $ret = $dop->_Start_Stop_Kill_Restart_Contaner($cid,"stop",5);

    my $ret_code = $ret->{_rc};

    #print $ret_code."\n";

    if($ret_code == 304){
    	print "Container $cid already stopped!\n";
    }elsif($ret_code == 404){
    	print "No such container!\n";
	exit;
    }elsif($ret_code == 500){
    	print "Server error!\n";
	exit;
    }else{
    	print "Stop container success!\n";
    }


    #删除容器
    $ret = $dop->_Remove_Container($cid);
    #print Dumper($ret);
    #exit;
    $ret_code = $ret->{_rc};

    if($ret_code == 400){
	die "Bad parameter!\n";
    }elsif($ret_code == 404){
	die "No such container!\n";
    }elsif($ret_code == 500){
	die "Server error!\n";
    }else{
	#删除容器的同时，删除数据库相关信息
	$mop->_Delete_Container_Info($dbh,$cid);
	print "Remove container $cid  success!\n";
    }
    
}


#--------------对容器执行命令-----------------
sub Exec_Cmd{
    my ($cid,$cmd) = @_;
    my $exec_cmd = {
	AttachStdin => true,
	AttachStdout => true,
	AttachStderr => true,
	Tty => false,
	Cmd => [
	    $cmd
	    ]
    };


    my $ret = $dop->_Exec_Create($cid,$exec_cmd);
    my $exec_instance_id = substr($ret->{'content'}->{'Id'},0,12);
    print $exec_instance_id."\n";
    #sleep(5);
    my $value = $dop->_Exec_Start($exec_instance_id);
    print Dumper($value);
    #exit;
}


#---debug

#$dop->_Container_info_by_id_name_ref('9147063e8e95');

#-------------关闭数据库连接--------------
$dbh->disconnect;                                #断开
